package my.xh.validate;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import my.convert.Map2Bean;
import my.xh.validate.handler.ValidateHandler;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * 验证注解处理类
 *
 * @author zhoufeng
 */
@Component
@Aspect
public class ValidateAspect {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private Map<String, ValidateHandler> validateHandlerMap;
    @Autowired
    private Map2Bean instance;

    /**
     * 使用AOP对使用了ValidateGroup的方法进行代理校验
     *
     * @throws Throwable
     */
    @SuppressWarnings({"finally", "rawtypes"})
    @Around("@annotation(my.xh.validate.ValidateField) || @annotation(my.xh.validate.ValidateGroup)")
    public Object validateAround(ProceedingJoinPoint joinPoint) throws Throwable {
        boolean flag = false;
        ValidateGroup an = null;
        CustomException customException = null;
        Object[] args = null;
        Method method = null;
        Object target = null;
        String methodName = null;
        try {
            Signature signature = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) signature;
            method = methodSignature.getMethod();//被代理过的方法
            Class<?>[] parameterTypes = method.getParameterTypes();
            methodName = signature.getName();
            target = joinPoint.getTarget();
            //com.example.demo.DemoApplication$$EnhancerBySpringCGLIB$$272fee2f
            Class clazz = target.getClass();
            if (clazz.getSimpleName().contains("$$Enhancer")) {
                clazz = clazz.getSuperclass();
            }
            method = getMethodByClassAndName(clazz, methodName, parameterTypes); // 得到拦截的方法
            args = joinPoint.getArgs(); // 方法的参数
            customException = getAnnotationByMethod(method, CustomException.class);
            an = getAnnotationByMethod(method, ValidateGroup.class);
            ValidateField[] fs = null;
            if (an == null) {
                ValidateField[] fs2 = {(ValidateField) getAnnotationByMethod(method, ValidateField.class)};
                fs = fs2;
            } else {
                fs = an.value();
            }
            flag = validateField(clazz, method, fs, args);
        } catch (Exception e) {
            logger.debug(e.toString());
            flag = false;
        } finally {
            if (flag) {
                logger.debug("参数验证通过");
            } else {
                logger.debug("参数验证未通过");
                Map<String, Object> map = V.vr.get();

                V.vr.remove();//清除当前线程的内存占用
                if (map != null) {
                    Set<Entry<String, Object>> entrySet = map.entrySet();
                    StringBuilder sb = new StringBuilder();
                    for (Entry<String, Object> entry : entrySet) {
                        sb.append(entry.getValue());
                        sb.append(",");
                    }
                    if (customException != null) {
                        Class<? extends Exception> excepClass = customException.excptionClazz();
                        String fieldName = customException.messageField();
                        Exception exception = excepClass.newInstance();

                        Field detailMessage = getField(excepClass, fieldName);
                        detailMessage.setAccessible(true);
                        detailMessage.set(exception, sb.substring(0, sb.length() - 1));
                        throw exception;
                    }
                    throw new IllegalArgumentException(sb.substring(0, sb.length() - 1));
                }
            }
            return joinPoint.proceed(args);
        }
    }

    /**
     * 验证参数是否合法
     *
     * @param method
     * @param class1
     */
    public boolean validateField(Class<? extends Object> class1, Method method, ValidateField[] valiedatefiles,
                                 Object[] args) throws Exception {
        V.vr.remove();
        HashMap<String, Object> map1 = new HashMap<String, Object>();
        boolean flag = true;
        String[] paramNames = GetParamNamesByJavassist.getParamName(class1, method);
        Object arg = null;
        StringBuilder paramName = new StringBuilder();
        for (ValidateField validateField : valiedatefiles) {
            paramName.delete(0, paramName.length());//清零
            //arg 和 paramName 的获取处理
            arg = getParamNameAndArg(class1, method, args, paramNames, arg, paramName, validateField);

            flag = validateHandlerMap.get("notNullValidateHandler").validate(validateField, paramName, arg, map1, flag);// 判断参数是否可以为空

            if (arg == null || "".equals(arg)) {
                if (!StringUtils.isEmpty(validateField.defalut()) && validateField.notNull() == false) { // 如果默认值项不为空 ,且notNull为false
                    setDefalutValue(arg, validateField, args, paramName, method);
                }
                continue;//参数为空时，就不用判断后面的了 ，直接跳过
            }

            for (Entry<String, ValidateHandler> entry : validateHandlerMap.entrySet()) {
                if ("notNullValidateHandler".equals(entry.getKey())) continue;
                flag = entry.getValue().validate(validateField, paramName, arg, map1, flag);
            }

        }

        if (!flag) {//只要有一项通不过，把flag设为假，那么就校验就没通过
            V.vr.set(map1);
        } else {
            //校验通过
        }
        return flag;
    }

    private void setDefalutValue(Object arg, ValidateField validateField, Object[] args, StringBuilder paramName, Method method) throws Exception {

        if (!"".equals(validateField.filedName())) {
            String param = paramName.toString().split("\\.")[1];
            Field field = getField(args[validateField.index()].getClass(), param);
            Object newValue = instance.convertValueType(field.getType(), validateField.defalut());
            field.set(args[validateField.index()], newValue);
        } else if (!"".equals(validateField.key())) {
            Map<String, Object> arg1 = (Map<String, Object>) args[validateField.index()];
            arg1.put(validateField.key(), validateField.defalut());
        } else {
            Class<?> parameterType = method.getParameterTypes()[validateField.index()];
            arg = instance.convertValueType(parameterType, validateField.defalut());
            args[validateField.index()] = arg;
        }
    }


    private Object getParamNameAndArg(Class<? extends Object> class1, Method method, Object[] args, String[] paramNames,
                                      Object arg, StringBuilder paramName, ValidateField validateField)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        if ("".equals(validateField.filedName()) && "".equals(validateField.key())) {
            arg = args[validateField.index()];
            paramName.append(paramNames[validateField.index()]);
        } else if (!"".equals(validateField.filedName()) && "".equals(validateField.key())) {//对象属性处理
            arg = getFieldByObjectAndFileName(args[validateField.index()], validateField.filedName());
            paramName.append(paramNames[validateField.index()]).append(".").append(validateField.filedName());
        } else if (!"".equals(validateField.key()) && "".equals(validateField.filedName())) {//map的key 处理
            Map<String, Object> data = (Map<String, Object>) args[validateField.index()];
            arg = data.get(validateField.key());
            paramName.append(validateField.key());
        } else {
            //配置不正确
            throw new RuntimeException(class1.getName() + "的" + method.getName() + "方法上:验证注解配置不正解，不能同时配filedName和key");
        }
        return arg;
    }

    /**
     * 根据对象和属性名得到 属性的值
     */
    public Object getFieldByObjectAndFileName(Object targetObj, String fileName) throws SecurityException,
            NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        String tmp[] = fileName.split("\\.");
        Object arg = targetObj;
        for (int i = 0; i < tmp.length; i++) {
            Method methdo = arg.getClass().getMethod(getGetterNameByFiledName(tmp[i]));
            arg = methdo.invoke(arg);
        }
        return arg;
    }

    /**
     * 根据属性名 得到该属性的getter方法名
     */
    public String getGetterNameByFiledName(String fieldName) {
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    /**
     * 根据目标方法和注解类型 得到该目标方法的指定注解
     */
    public <T> T getAnnotationByMethod(Method method, Class<T> annoClass) {
        Annotation all[] = method.getAnnotations();
        for (Annotation annotation : all) {
            if (annotation.annotationType() == annoClass) {
                return (T) annotation;
            }
        }
        return null;
    }

    /**
     * 根据类和方法名得到方法
     *
     * @param parameterTypes
     */
    public Method getMethodByClassAndName(Class c, String methodName, Class<?>[] parameterTypes) {
        Method[] methods = c.getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class<?>[] parameterTypes2 = method.getParameterTypes();
                if (parameterTypes != null && parameterTypes2 != null && parameterTypes2.length == parameterTypes.length) {
                    for (int i = 0; i < parameterTypes.length; i++) {
                        if (!parameterTypes[i].equals(parameterTypes2[i])) return null;
                    }
                    return method;
                }
            }
        }
        return null;
    }

    private Field getField(Class clazz, String fieldName) {
        Field declaredField = null;
        while (declaredField == null && clazz != null) {
            try {
                declaredField = clazz.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e) {
            }
            clazz = clazz.getSuperclass();
        }
        if (declaredField != null) declaredField.setAccessible(true);
        return declaredField;
    }

}